﻿using System;
using System.IO;
using System.Linq;
using System.Text;

namespace AiMoWindows.GHAiMo
{
    static class GHScriptIO

    {
        #region ScriptIO

        readonly static string SectionHeader = "[DFEFO-9742-3HF4-EGE]";
        readonly static string ParamInfoSpliter = "[5-4D9A-AB77-AA7EDD]";
        readonly static string GHScriptExtension = ".GHScriptSource";
        public static string GHFolder { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AimoRhino", "Debug", "GH");
        public static string GetFileNameFromPipeName(string pipeName)
        {
            if (!Directory.Exists(GHFolder)) Directory.CreateDirectory(GHFolder);
            return Path.Combine(GHFolder, pipeName + GHScriptExtension);
        }
        public static string GetSameScriptPath(string dllPath)
        {
            return dllPath.Replace("dll", GHScriptExtension);
        }
        public static bool ReadFromCache(string fileName, out string[] usings, out string[] mains, out string[] funcs, out string[] classes, out string[] externalReferences,
            out GH_ParamSyntaxNodeInfo[] paramInfos, out bool hasPublicCustomizedClasses)
        {
            usings = Array.Empty<string>();
            mains = Array.Empty<string>();
            funcs = Array.Empty<string>();
            classes = Array.Empty<string>();
            externalReferences = Array.Empty<string>();
            paramInfos = Array.Empty<GH_ParamSyntaxNodeInfo>();
            hasPublicCustomizedClasses = false;
            try
            {
                if (!File.Exists(fileName))
                {
                    return false;
                }

                using (StreamReader reader = new StreamReader(fileName, Encoding.UTF8))
                {
                    usings = ReadSection(reader);
                    mains = ReadSection(reader);
                    funcs = ReadSection(reader);
                    classes = ReadSection(reader);
                    externalReferences = ReadSection(reader);
                    string[] paramInfosString = ReadSection(reader);
                    paramInfos = ReadParamInfos(paramInfosString);
                    string[] hasPublicCustomizedClassesStrings = ReadSection(reader);
                    if (hasPublicCustomizedClassesStrings != null && hasPublicCustomizedClassesStrings.Length == 2)
                    {
                        bool.TryParse(hasPublicCustomizedClassesStrings[1], out hasPublicCustomizedClasses);
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
#if !RELEASE
                System.Diagnostics.Debug.WriteLine("ReadFromCache Error: " + ex);
#endif
                return false;
            }
        }

        public static void WriteToCache(string[] usings, string[] mains, string[] funcs, string[] classes, string[] externalReferences,
            GH_ParamSyntaxNodeInfo[] paramInfos, string fileName, bool hasPublicCustomizedClasses)
        {
            try
            {
                using (StreamWriter writer = new StreamWriter(fileName, false, Encoding.UTF8))
                {
                    WriteSection(writer, usings);
                    WriteSection(writer, mains);
                    WriteSection(writer, funcs);
                    WriteSection(writer, classes);
                    WriteSection(writer, externalReferences);
                    WriteSection(writer, WriteParamInfos(paramInfos));
                    WriteSection(writer, new string[] { "HasPublicCustomizedClasses", hasPublicCustomizedClasses.ToString() });
                }
            }
            catch (Exception ex)
            {
#if !RELEASE
                System.Diagnostics.Debug.WriteLine("WriteToCache Error: " + ex);
#endif
            }
        }
        public static string[] WriteParamInfos(GH_ParamSyntaxNodeInfo[] paramInfos)
        {
            return paramInfos.Select(x =>
                string.Join(
                    ParamInfoSpliter,
                    x.Index.ToString(),
                    x.MethodName,
                    x.ParamName,
                    x.TypeName)
            ).ToArray();
        }

        public static GH_ParamSyntaxNodeInfo[] ReadParamInfos(string[] infoStrings)
        {
            return infoStrings.Select(infoString =>
            {
                string[] parts = infoString.Split(new string[] { ParamInfoSpliter }, StringSplitOptions.None);
                if (parts.Length != 4)
                {
                    throw new ArgumentException("Invalid GH_ParamSyntaxNodeInfo infoStrings format");
                }
                return new GH_ParamSyntaxNodeInfo(int.Parse(parts[0]), parts[1], parts[2], parts[3]);
            }).ToArray();
        }
        static string[] ReadSection(StreamReader reader)
        {
            string sectionHeader = reader.ReadLine() ?? throw new InvalidDataException("Null section header");
            if (sectionHeader != SectionHeader) throw new InvalidDataException("Invalid section header");
            int count = int.Parse(reader.ReadLine() ?? throw new InvalidDataException("Invalid content of m_PrintCount"));
            string[] section = new string[count];
            for (int i = 0; i < count; i++)
            {
                section[i] = reader.ReadLine() ?? throw new InvalidDataException("Invalid content of section");
            }
            return section;
        }
        static void WriteSection(StreamWriter writer, string[] section)
        {
            writer.WriteLine(SectionHeader);
            writer.WriteLine(section.Length);
            foreach (string line in section)
            {
                writer.WriteLine(line);
            }
        }
        #endregion
    }
}
